
#include <gfx/camera.h>
#include <gfx/light.h>
#include <gfx/imesh.h>
#include <gfx/renderenvironment.h>
#include <scene/ianimator.h>
#include <scene/itransformationcallback.h>
#include <scene/entitynode.h>
#include <scene/geometry.h>
#include <scene/light.h>
#include <scene/sector.h>
#include <assert.h>

ceEntityNode::ceEntityNode()
  : ceNode ()
  , _geometry(0)
  , _light(0)
  , _camera(0)
  , _shouldRender (false)
	, _numLights (0)
  , _castShadow (false)
{
	for (unsigned i=0; i<MaxLights; i++)
		{
			_lights[i] = 0;
		}
}

ceEntityNode::~ceEntityNode()
{
  CE_UNSET(_geometry);
}

ceNodeType ceEntityNode::GetNodeType() const
{
  return NT_EntityNode;
}

void ceEntityNode::UpdateTransformation()
{
  if (_light)
    {
      ceVector3f p = GetGlobalTranslation();
			_light->GetLight()->SetPosition(p);
    }

  if (_camera)
    {
      const ceMatrix4f& mat = GetGlobalMatrix();
      ceVector3f eye (mat.GetTranslation());
      ceVector3f spot (eye + mat.GetYAxis() * 100.0f);

      _camera->SetEye(eye);
      _camera->SetSpot(spot);
      _camera->SetUp(mat.GetZAxis());
      _camera->FinishTransformation();
    }
}


void ceEntityNode::FillPrivateBoundingBox(ceBoundingBox &bbox)
{
  ceNode::FillPrivateBoundingBox (bbox);

  if (_geometry)
    {
      bbox.Add(GetGlobalMatrix(), _geometry->GetBoundingBox());
    }
}

void ceEntityNode::AddEffectCamera(iEffectCamera *camera)
{
  _effectCameras.push_back (camera);
}

unsigned ceEntityNode::GetNumberOfEffectCameras() const
{
  return _effectCameras.size ();
}

iEffectCamera* ceEntityNode::GetEffectCamera(unsigned idx)
{
  assert (idx < _effectCameras.size ());
  return _effectCameras[idx];
}

const iEffectCamera* ceEntityNode::GetEffectCamera(unsigned idx) const
{
  assert (idx < _effectCameras.size ());
  return _effectCameras[idx];
}

void ceEntityNode::SetGeometry(ceGeometry *geometry)
{
  CE_SET(_geometry, geometry);
}

ceGeometry* ceEntityNode::GetGeometry()
{
  return _geometry;
}

const ceGeometry* ceEntityNode::GetGeometry() const
{
  return _geometry;
}

void ceEntityNode::SetLight(ceLightObject *light)
{
  _light = light;
  // nodes that have a light attached must not be clippped
  // this would remove the abbility to light the scene.
  SetClip(!_light);
}

ceLightObject* ceEntityNode::GetLight()
{
  return _light;
}

const ceLightObject* ceEntityNode::GetLight() const
{
  return _light;
}

void ceEntityNode::SetCamera(ceCamera *camera)
{
  _camera = camera;
}

ceCamera* ceEntityNode::GetCamera()
{
  return _camera;
}

const ceCamera* ceEntityNode::GetCamera() const
{
  return _camera;
}

void ceEntityNode::SetLit (bool lit)
{
	_lit = lit;
}

bool ceEntityNode::IsLit () const
{
	return _lit;
}

void ceEntityNode::ClearLights ()
{
	_numLights = 0;
}

void ceEntityNode::AddLight (ceLight* light)
{
	if (_numLights + 1 >= MaxLights)
		{
			return;
		}
	_lights[_numLights++] = light;
}

ceLight* ceEntityNode::GetLight (unsigned idx)
{
	assert (idx < _numLights);
	return _lights[idx];
}

const ceLight* ceEntityNode::GetLight (unsigned idx) const
{
	assert (idx < _numLights);
	return _lights[idx];
}

unsigned ceEntityNode::GetNumberOfLights () const
{
	return _numLights;
}

void ceEntityNode::SetCastShadow(bool castShadow)
{
  _castShadow = castShadow;
}

bool ceEntityNode::CastsShadow() const
{
  return _castShadow;
}

void ceEntityNode::MarkForRendering(bool shouldRender)
{
  _shouldRender = shouldRender;
}

bool ceEntityNode::ShouldRender() const
{
  return _shouldRender;
}

void ceEntityNode::Update(ceRenderEnvironment &env, iDevice* device)
{
  ceNode::Update(env, device);

  env.CameraLocation = GetGlobalMatrixInverted() * env.CameraLocationGlobal;
  env.ModelMatrix = GetMatrix();
  if (_geometry)
    {
      _geometry->GetMesh()->Update(env, device);
    }
}


void ceEntityNode::FindLights (ceSector* sector)
{
	if (_light)
		{
			sector->AddLight (_light->GetLight ());
		}
}


void ceEntityNode::TestAndAddLight (ceLight* light)
{
	// TODO: Do the testing if the light is needed
	AddLight (light);
}


